package com.edan.partnerback.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.edan.partnerback.common.VO.DynamicCache;
import com.edan.partnerback.common.VO.Result;
import com.edan.partnerback.entity.Dynamic;
import com.edan.partnerback.entity.Praise;
import com.edan.partnerback.entity.User;
import com.edan.partnerback.exception.ServiceException;
import com.edan.partnerback.service.ICommentsService;
import com.edan.partnerback.service.IDynamicService;
import com.edan.partnerback.service.IPraiseService;
import com.edan.partnerback.service.IUserService;
import com.edan.partnerback.service.impl.RestHighLevelClientImpl;
import com.edan.partnerback.utils.RedisCache;
import com.edan.partnerback.utils.SessionUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author edan
 * @since 2024-03-28
 */
@Api(tags = "动态管理")
@RestController
@RequestMapping("/dynamic")
@Slf4j
@SuppressWarnings("all")
public class DynamicController {

    @Resource
    IUserService userService;
    @Resource
    RedisCache redisCache;
    @Resource
    RedisTemplate redisTemplate;
    @Resource
    ICommentsService commentsService;
    @Resource
    IPraiseService praiseService;
    @Resource
    RestHighLevelClientImpl<Dynamic> client;
    @Resource
    private IDynamicService dynamicService;

    @GetMapping("/all")
    public Result findAll() {
        return Result.success(dynamicService.list());
    }

    @GetMapping("/{id}")
    @SaIgnore
    public Result findOne(@PathVariable Integer id) {
        User user = SessionUtils.getUser();
        Dynamic dynamic = dynamicService.getById(id);
        Optional.of(userService.getOne(new QueryWrapper<User>().eq("uid", dynamic.getUid()))).ifPresent(dynamic::setUser);
        // 注入浏览数， 点赞数， 评论
        Double views = redisTemplate.opsForZSet().score(DynamicCache.DYNAMICVIEW, id);
        Double praise = redisTemplate.opsForZSet().score(DynamicCache.DYNAMICPRAISE, id);
        Double comments = redisTemplate.opsForZSet().score(DynamicCache.DYNAMICCOMMENT, id);
        dynamic.setViews(views == null ? 0 : views.intValue());
        dynamic.setPraise(praise == null ? 0 : praise.intValue());
        dynamic.setComment(comments == null ? 0 : comments.intValue());
        // List<Comments> comments = commentsService.list(new QueryWrapper<Comments>().eq("dynamic_id", id));
        // dynamic.setComments(comments);
        // dynamic.setComment(comments.size());
        // 如果是作者本人，则加入点赞人列表
        if (user != null && user.getUid().equals(dynamic.getUid())) {
            List<Praise> praises = praiseService.list(new QueryWrapper<Praise>().eq("type", "dynamic")
                    .eq("fid", dynamic.getId())
                    .eq("user_id", user.getUid()));
            dynamic.setPraises(praises);
        } else if (user != null) {
            Praise one = praiseService.getOne(new LambdaQueryWrapper<Praise>().eq(Praise::getFid, dynamic.getId()).eq(Praise::getUserId, user.getId()));
            if (one != null) {
                dynamic.setIsPraise(true);
            }
            //收藏同理
        }
        return Result.success(dynamic);
    }

    @PostMapping
    public Result save(@RequestBody Dynamic dynamic) {
        //获取当前登录用户信息
        User user = SessionUtils.getUser();
        Assert.notNull(user, () -> {
            throw new ServiceException("用户未登录");
        });
        dynamic.setUid(user.getUid());
        dynamicService.save(dynamic);
        if (!CollUtil.isEmpty(user.getDynastyIds())) {
            user.getDynastyIds().add(dynamic.getId());
            StpUtil.getSession().set("userInfo", user);
        }
        return Result.success();
    }

    // @AutoLog("编辑${moduleName}")
    @PutMapping
    public Result update(@RequestBody Dynamic dynamic) {
        dynamicService.updateById(dynamic);
        return Result.success();
    }

    // @AutoLog("删除${moduleName}")
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
        dynamicService.removeById(id);
        return Result.success();
    }

    // @AutoLog("批量删除${moduleName}")
    @PostMapping("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer> ids) {
        dynamicService.removeByIds(ids);
        return Result.success();
    }

    /**
     * @param isMe     判断是否搜索自己的动态
     * @param search
     * @param pageNum
     * @param pageSize
     * @return
     */
    @ApiOperation(value = "首页分页数据")
    @GetMapping("/page")
    @SaIgnore
    public Result findPage(
            @RequestParam(defaultValue = "false") Boolean isMe,
            @RequestParam(defaultValue = "") String search,
            @RequestParam Integer pageNum,
            @RequestParam Integer pageSize) {
        Page<Dynamic> page = new Page<>(pageNum, pageSize);
        if ("".equals(search)) {//按最新排序
            QueryWrapper<Dynamic> queryWrapper = new QueryWrapper<Dynamic>()
                    .select("id", "name", "imgs", "description", "create_time", "update_time", "uid", "views", "tags")
                    .orderByDesc("create_time");
            //获取所有动态
            dynamicService.page(page, queryWrapper);
            List<Dynamic> records = page.getRecords();
        } else {
            List<Dynamic> list = client.findAll("partner-dynamic", search, pageNum, pageSize);
            User user = SessionUtils.getUser();
            if (user != null && isMe) {
                list = list.stream().filter(dynamic -> user.getUid().equals(dynamic.getUid())).collect(Collectors.toList());
            }
            page.setRecords(list);
        }
        inject(page.getRecords());
        return Result.success(page);
    }

    private void inject(List<Dynamic> records) {
        List<User> list = userService.list();
        Set<ZSetOperations.TypedTuple<Integer>> views = redisCache.getCacheZSet(DynamicCache.DYNAMICVIEW);
        Set<ZSetOperations.TypedTuple<Integer>> praises = redisCache.getCacheZSet(DynamicCache.DYNAMICPRAISE);
        Set<ZSetOperations.TypedTuple<Integer>> comments = redisCache.getCacheZSet(DynamicCache.DYNAMICCOMMENT);
        for (Dynamic record : records) {
            //注入user对象
            list.stream().filter((user) -> user.getUid().equals(record.getUid())).findFirst().ifPresent(record::setUser);
            //注入浏览数
            views.stream().filter(view -> view.getValue().equals(record.getId()))
                    .findFirst().ifPresent(view -> record.setViews(view.getScore().intValue()));
            //注入点赞数
            praises.stream().filter(praise -> praise.getValue().equals(record.getId()))
                    .findFirst().ifPresent(praise -> record.setPraise(praise.getScore().intValue()));
            //注入评论数
            comments.stream().filter(comment -> comment.getValue().equals(record.getId()))
                    .findFirst().ifPresent(comment -> record.setComment(comment.getScore().intValue()));
        }
    }

    @ApiOperation(value = "获取热门动态", notes = "支持游客访问")
    @GetMapping("/hot/{type}")
    @SaIgnore
    public Result hot(@PathVariable(required = false) String type) {
        List<String> types = new ArrayList<>();
        if (type.equals("all")) {
            types.addAll(Arrays.asList(DynamicCache.DYNAMICVIEW, DynamicCache.DYNAMICPRAISE, DynamicCache.DYNAMICCOMMENT));
        } else if (!type.contains("Change")) {
            List<Dynamic> dynamicList = (List<Dynamic>) redisTemplate.opsForHash().get(DynamicCache.DYNAMICbLOG, "dynamic:" + type + ":rank");
            if (!CollUtil.isEmpty(dynamicList)) {
                return Result.success(new HashMap<String, List<Dynamic>>() {{
                    put(type + ":rank", dynamicList);
                }});
            }
            types.add("dynamic:" + type);
        } else {
            types.add("dynamic:" + type.replace("Change", ""));
        }
        HashMap<String, List<Dynamic>> res = new HashMap<String, List<Dynamic>>();
        HashMap<String, List<Integer>> resIds = new HashMap<>();
        List<Integer> ids = new ArrayList<>();
        for (String s : types) {
            Set<Integer> set = redisTemplate.opsForZSet().reverseRange(s, 0, 9);

            //添加id
            ids.addAll(set);
            resIds.put(s + ":rank", new ArrayList<>(set));
        }
        ids = new ArrayList<>(new HashSet<>(ids));
        List<Dynamic> dynamics = dynamicService.listByIdsPart(ids);
        resIds.forEach((k, v) -> {
            res.put(k, v.stream().map(value -> dynamics.stream().filter(dynamic -> dynamic.getId().equals(value)).findFirst().get()).collect(Collectors.toList()));
        });
        // 异步写入缓存
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(4, 5, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5));
        resIds.forEach((k, v) -> {
            poolExecutor.execute(() -> {
                redisTemplate.delete(k);
                redisCache.setCacheList(k, v);
            });
        });
        poolExecutor.execute(() -> {
            redisTemplate.opsForHash().putAll(DynamicCache.DYNAMICbLOG, res);
        });
        poolExecutor.shutdown();
        return Result.success(res);
    }

    @ApiOperation(value = "获取当前用户的动态", notes = "支持同时按一或多种排序方式，默认按创建时间排序，以英文逗号分割")
    @GetMapping("/mydynamic")
    public Result myDynamic(@RequestParam(defaultValue = "created_time") String order,
                            @RequestParam Integer pageNum,
                            @RequestParam Integer pageSize) {

        // 可以校验数据合法性，比如orderList必须是合法的排序字段
        List<String> orderList = StrUtil.split(order, ",");
        User user = SessionUtils.getUser();

        Page<Dynamic> page;
        List<Dynamic> records;
        //缓存非空
        if (!CollUtil.isEmpty(user.getDynastyIds())) {
            List<Dynamic> dynamics = dynamicService.listByIds(user.getDynastyIds());
            page = new Page<>(pageNum, pageSize);
            page.setRecords(dynamics);
            records = dynamics;
        } else {
            QueryWrapper<Dynamic> wrapper = new QueryWrapper<Dynamic>()
                    .eq("uid", user.getUid())
                    .orderByDesc(orderList);
            page = dynamicService.page(new Page<>(pageNum, pageSize), wrapper);
            records = page.getRecords();
            for (Dynamic record : records) {
                if (user.getDynastyIds() == null) {
                    user.setDynastyIds(new ArrayList<>());
                }
                user.getDynastyIds().add(record.getId());
            }
            StpUtil.getSession().set("userInfo", user);
        }
        inject(records);
        return Result.success(page);
    }

    /**
     * 导出接口
     */
    @GetMapping("/export")
    public void export(HttpServletResponse response) throws Exception {
        // 从数据库查询出所有的数据
        List<Dynamic> list = dynamicService.list();
        // 在内存操作，写出到浏览器
        ExcelWriter writer = ExcelUtil.getWriter(true);

        // 一次性写出list内的对象到excel，使用默认样式，强制输出标题
        writer.write(list, true);

        // 设置浏览器响应的格式
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        String fileName = URLEncoder.encode("Dynamic信息表", "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");

        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);
        out.close();
        writer.close();

    }

    /**
     * excel 导入
     *
     * @param file
     * @throws Exception
     */
    @PostMapping("/import")
    public Result imp(MultipartFile file) throws Exception {
        InputStream inputStream = file.getInputStream();
        ExcelReader reader = ExcelUtil.getReader(inputStream);
        // 通过 javabean的方式读取Excel内的对象，但是要求表头必须是英文，跟javabean的属性要对应起来
        List<Dynamic> list = reader.readAll(Dynamic.class);

        dynamicService.saveBatch(list);
        return Result.success();
    }

}
